﻿using System;
using System.Diagnostics;
using System.IO;
using log4net;
using Microsoft.BizTalk.Component.Interop;
using AppFx.BizTalk.Container;
using Autofac;
using AppFx.BizTalk.Dynamics.CRM.Helpers.CRMDataExtract;
using AppFx.BizTalk.IO.Streams;
using AppFx.BizTalk.PipelineComponents;

namespace AppFx.BizTalk.Dynamics.CRM.PipelineComponents
{
    [ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
    [ComponentCategory(CategoryTypes.CATID_Any)]
    [System.Runtime.InteropServices.Guid(ComponentGuid)]
    public class CRMStreamedDataExtract : BaseCustomPipelineComponent, IComponent, IComponentUI, IPersistPropertyBag, IBaseComponent
    {
        private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private const string ComponentGuid = "C89EB4EA-DCAF-4E41-8126-7B330055DFB6";
        private const string ComponentDescription = "Download from Dynamics CRM with a fetch xml query in a streaming manner";
        private const string ComponentName = "CRMStreamedDataExtract";

        public CRMStreamedDataExtract()
            : base(ComponentName, typeof(CRMStreamedDataExtract), ComponentDescription)
        {

        }

        public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute(IPipelineContext pContext, Microsoft.BizTalk.Message.Interop.IBaseMessage pInMsg)
        {
            var bodyPartStream = pInMsg.BodyPart.GetOriginalDataStream();

            //Read the Fetch Xml Request from the original stream
            var streamReader = new StreamReader(bodyPartStream);
            var fetchXmlRequest = streamReader.ReadToEnd();
            bodyPartStream.Seek(0, SeekOrigin.Begin);

            LogProperty("XmlWrapperStart", XmlWrapperStart);
            LogProperty("XmlWrapperStartExtension", XmlWrapperStartExtension);
            LogProperty("XmlWrapperEnd", XmlWrapperEnd);
            LogProperty("FetchCount", FetchCount.ToString());
            LogProperty("ContainerName", ContainerName);
            LogProperty("FetchXmlManipulator", FetchXmlManipulator);

            var container = new BizTalkApplicationContainer(ContainerName);

            //Manipulate Fetch Xml
            if(string.IsNullOrEmpty(FetchXmlManipulator) == false)
            {
                using (var scope = container.Container.BeginLifetimeScope())
                {
                    var fetchXmlManipulator = scope.ResolveNamed<IFetchXmlManipulator>(FetchXmlManipulator);
                    if(fetchXmlManipulator == null)
                    {
                        var errorMessage = string.Format("The fetch xml manipulator {0} is not in the container", FetchXmlManipulator);
                        Log.Error(errorMessage);
                        throw new ApplicationException(errorMessage);
                    }
                    fetchXmlRequest = fetchXmlManipulator.ManipulateFetchXml(fetchXmlRequest, pInMsg);
                }
            }

            //Get Start Wrapper Extension
            var modifiedXmlStart = XmlWrapperStart;
            if (string.IsNullOrEmpty(XmlWrapperStartExtension))
                Log.Info("No XmlWrapperStartExtension extensions have been specified");            
            else
            {
                using (var scope = container.Container.BeginLifetimeScope())
                {
                    var extension = scope.ResolveNamed<ICrmDownloadXmlWrapperManipulator>(XmlWrapperStartExtension);
                    if (extension == null)
                        Log.WarnFormat("The XmlWrapperStartExtension {0} was not found in the container",
                            XmlWrapperStartExtension);
                    else
                    {
                        modifiedXmlStart = extension.AmendXmlWrapper(modifiedXmlStart, pInMsg);
                        Log.InfoFormat("The modified xml wrapper is {0}", modifiedXmlStart);
                    }
                }
            }

            //Setup CRM Wrapper
            var crmWrapper = new CRMDownloadManager(CrmConnectionString, fetchXmlRequest)
            {
                MessageEnd = XmlWrapperEnd,
                MessageStart = modifiedXmlStart,
                FetchCount =  FetchCount
            };

            //Replace body part stream with CRM wrapper stream
            Log.Debug("Starting CRM Extract Download");
            var crmDownloadStream = new GenericDownloadStream(crmWrapper);
            pInMsg.BodyPart.Data = crmDownloadStream;
            pContext.ResourceTracker.AddResource(crmDownloadStream);

            return pInMsg;
        }

        private void LogProperty(string name, string propertyValue)
        {
            if(string.IsNullOrEmpty(propertyValue))
                Log.DebugFormat("Property: {0}, Value: Null or empty", name);
            else
                Log.DebugFormat("Property: {0}, Value: {1}", name, propertyValue);
        }
       

        #region Component Properties

        private int _fetchCount = 2000;
        private string _containerName;
        private string _xmlWrapperStart;
        private string _xmlWrapperStartExtension;
        private string _xmlWrapperEnd;
        private string _crmConnectionString;
        private string _fetchXmlManipulator;

        public string FetchXmlManipulator
        {
            get { return _fetchXmlManipulator; }
            set { _fetchXmlManipulator = value; }
        }

        public string CrmConnectionString
        {
            get { return _crmConnectionString; }
            set { _crmConnectionString = value; }
        }

        public string XmlWrapperEnd
        {
            get { return _xmlWrapperEnd; }
            set { _xmlWrapperEnd = value; }
        }

        public string XmlWrapperStartExtension
        {
            get { return _xmlWrapperStartExtension; }
            set { _xmlWrapperStartExtension = value; }
        }

        public string XmlWrapperStart
        {
            get { return _xmlWrapperStart; }
            set { _xmlWrapperStart = value; }
        }

        public string ContainerName
        {
            get { return _containerName; }
            set { _containerName = value; }
        }

        public int FetchCount
        {
            get { return _fetchCount; }
            set { _fetchCount = value; }
        }
        
        #endregion

        public IntPtr Icon
        {
            get { return new IntPtr(); }
        }

        public System.Collections.IEnumerator Validate(object projectSystem)
        {
            return null;
        }

        // ReSharper disable once InconsistentNaming
        public void GetClassID(out Guid classID)
        {
            classID = new Guid(ComponentGuid);
        }

        public void InitNew()
        {
            //Do nothing
        }

        public void Load(IPropertyBag propertyBag, int errorLog)
        {
            try
            {
                base.ReadPropertyBag(propertyBag, "CrmConnectionString", ref _crmConnectionString);
                base.ReadPropertyBag(propertyBag, "ContainerName", ref _containerName);
                base.ReadPropertyBag(propertyBag, "XmlWrapperStart", ref _xmlWrapperStart);
                base.ReadPropertyBag(propertyBag, "XmlWrapperEnd", ref _xmlWrapperEnd);
                base.ReadPropertyBag(propertyBag, "XmlWrapperStartExtension", ref _xmlWrapperStartExtension);
                base.ReadPropertyBag(propertyBag, "FetchXmlManipulator", ref _fetchXmlManipulator);
                base.ReadPropertyBagAsInt(propertyBag, "FetchCount", ref _fetchCount);                
            }
            catch (Exception ex)
            {
                Log.Error(ex);
                throw;
            }
        }

        public void Save(IPropertyBag propertyBag, bool clearDirty, bool saveAllProperties)
        {
            try
            {
                base.WritePropertyBag(propertyBag, "ContainerName", _containerName);
                base.WritePropertyBag(propertyBag, "CrmConnectionString", _crmConnectionString);
                base.WritePropertyBag(propertyBag, "XmlWrapperStart", _xmlWrapperStart);
                base.WritePropertyBag(propertyBag, "XmlWrapperEnd", _xmlWrapperEnd);
                base.WritePropertyBag(propertyBag, "XmlWrapperStartExtension", _xmlWrapperStartExtension);
                base.WritePropertyBag(propertyBag, "FetchXmlManipulator", _fetchXmlManipulator);
                base.WritePropertyBag(propertyBag, "FetchCount", _fetchCount);
            }
            catch (Exception ex)
            {
                Log.Error(ex);
                throw;
            }
        }



        public string Description
        {
            get { return ComponentDescription; }
        }

        public string Name
        {
            get { return ComponentName; }
        }

        public string Version
        {
            get { return GetType().Assembly.GetName().Version.ToString(); }
        }
    }
}




